﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Diagnostics;
using System.Threading;
using Microsoft.Xna.Framework.Content;
using SpriteGame.Rendering;

namespace SpriteGame.Effects.Blood
{
    public class BloodParticleManager
    {

        private static BloodParticleManager instance;
        public static BloodParticleManager Instance
        {
            get
            {
                if (instance == null)
                    instance = new BloodParticleManager();
                return instance;
            }
        }
        private List<BloodParticle> liveParticles = new List<BloodParticle>();
        private Stack<BloodParticle> deadParticles = new Stack<BloodParticle>();
        private Stack<BloodParticleDef> particlesToAdd = new Stack<BloodParticleDef>();
        private Rectangle vp;

        private static readonly object locke = new object();

        private static readonly object inputListLock = new object();

        private RenderTarget2D renderTarget;
        private RenderTarget2D outputRT;

        private GraphicsDevice graphics;
        private SpriteBatch spriteBatch;
        private const float oneOverEight = 1f / 8f; // is this precomputed?

        private Effect bloodEffect;
        private Stopwatch stopwatch = new Stopwatch();

        private Texture2D bloodTexture;
        private Thread bloodThread;

        public BloodParticleManager()
        {
        }

        public void InitBloodParticleManager(int _maxParticles, int _vpWidth, int _vpHeight, GraphicsDevice _graphics, ContentManager _content)
        {
            graphics = _graphics;
            vp = new Rectangle(0, 0, _vpWidth, _vpHeight);
            renderTarget = new RenderTarget2D(_graphics, _vpWidth, _vpHeight);
            outputRT = new RenderTarget2D(_graphics, _vpWidth, _vpHeight);
            spriteBatch = new SpriteBatch(_graphics);
            bloodTexture = _content.Load<Texture2D>("Textures/BloodTexture");
            bloodEffect = _content.Load<Effect>("Effects/BloodEffect");
            for (int i = 0; i < _maxParticles; ++i)
            {
                deadParticles.Push(new BloodParticle(bloodTexture));
            }

            bloodThread = new Thread(new ThreadStart(BloodParticleManager.Instance.StartThread));
            bloodThread.Start();

        }

        public void SpawnParticle(Vector2 _position, Vector2 _velocity, float _scale)
        {
            lock (inputListLock)
            {
                particlesToAdd.Push(new BloodParticleDef(_position, _velocity, _scale));
            }
        }

        private const Int64 frameTime = (Int64)(1000 * (1.0f / 120.0f));
        Int64 time = 0;
        public void StartThread()
        {
            stopwatch.Start();
            while (true)
            {

                lock (locke)
                {
                    instance.update();

                    time = stopwatch.ElapsedMilliseconds;
                    stopwatch.Restart();
                }
                if (time < frameTime)
                    Thread.Sleep(TimeSpan.FromMilliseconds(frameTime - time));

            }
        }

        private void update()
        {
            lock (inputListLock)
            {
                BloodParticle p;
                while (particlesToAdd.Count > 0)
                {
                    if (deadParticles.Count > 0)
                    {
                        p = deadParticles.Pop();
                    }
                    else
                    {
                        p = liveParticles.First();
                        liveParticles.Remove(p);
                        //particles.RemoveAt(particles.Count - 1);
                    }
                    BloodParticleDef pd = particlesToAdd.Pop();
                    p.InitParticle(pd.position, pd.velocity, pd.scale);
                    liveParticles.Add(p);
                }
            }

            for (int i = 0; i < liveParticles.Count; ++i)
            {
                BloodParticle p = liveParticles[i];
                if (p.Alive)
                    p.Update(time);
                else
                {
                    liveParticles.Remove(p);
                    deadParticles.Push(p);
                }
            }
        }

        public void DrawBlood(ShapeRenderer _batch, Color _finalColor, RenderTarget2D _prevRenderTarget)
        {
            //lock (locke)
            //{                
            graphics.SetRenderTarget(renderTarget);
            graphics.Clear(Color.Transparent);
            if (liveParticles.Count > 0)
            {


                _batch.BeginBatch();

                lock (locke)
                {
                    foreach (BloodParticle b in liveParticles)
                    {
                        b.Draw(_batch);
                    }
                }

                _batch.EndBatch();
            }

                //spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive, null, null, null, null, _projection);

                //lock (locke)
                //{
                //    foreach (BloodParticle b in liveParticles)
                //    {
                //        b.Draw(spriteBatch);
                //    }
                //}
                //spriteBatch.End();

                graphics.SetRenderTarget(_prevRenderTarget);
                graphics.Clear(Color.Transparent);
                bloodEffect.Parameters["colorIn"].SetValue(_finalColor.ToVector4());
                spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.AnisotropicClamp, DepthStencilState.None, RasterizerState.CullNone, bloodEffect);
                spriteBatch.Draw(renderTarget, new Rectangle(0, 0, graphics.Viewport.Width, graphics.Viewport.Height), _finalColor);
                spriteBatch.End();

            //}
        }
    }
}
